<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="PUA86" id="PUA86"><span data-lake-id="u7d6fcae0" id="u7d6fcae0">典型回答</span></h1>
  <p data-lake-id="uc22f1947" id="uc22f1947"><br></p>
  <p data-lake-id="uc00e9b5d" id="uc00e9b5d"><span data-lake-id="u43f07023" id="u43f07023">方法区是Java虚拟机规范定义的一块用于存储类信息、常量、静态变量、编译器编译后的代码等数据的内存区域。</span></p>
  <p data-lake-id="u772a7247" id="u772a7247"><span data-lake-id="u0d498568" id="u0d498568">​</span><br></p>
  <p data-lake-id="u75630d3f" id="u75630d3f"><span data-lake-id="u286ee180" id="u286ee180">方法区是线程共享的，每个虚拟机实例只有一个方法区。</span><strong><span data-lake-id="u280452a7" id="u280452a7">实现方法区的方式在不同的JDK厂商以及不同版本中可能有所不同的。</span></strong><span data-lake-id="u16d21760" id="u16d21760">所谓</span><u><span data-lake-id="ubcc2471e" id="ubcc2471e">规范是规范，实现是实现，两码事儿。</span></u></p>
  <p data-lake-id="u86f8d104" id="u86f8d104"><span data-lake-id="u39c9fdd9" id="u39c9fdd9">​</span><br></p>
  <p data-lake-id="u36e24805" id="u36e24805"><span data-lake-id="u7e4a10de" id="u7e4a10de">我们拿主流的HotSpot虚拟机来说明一下他的实现机制。</span></p>
  <p data-lake-id="u6c6bfb31" id="u6c6bfb31"><br></p>
  <p data-lake-id="u49d15ad5" id="u49d15ad5"><span data-lake-id="u68a84a97" id="u68a84a97">在JDK 1.7及之前的版本中，方法区通常被实现为</span><strong><span data-lake-id="u93e1c18b" id="u93e1c18b">永久代（Permanent Generation）</span></strong><span data-lake-id="u8520e901" id="u8520e901">，用于存储类信息、常量池、静态变量、即时编译器编译后的代码等数据。</span></p>
  <p data-lake-id="u0ded4e9b" id="u0ded4e9b"><span data-lake-id="uf142a646" id="uf142a646">​</span><br></p>
  <p data-lake-id="u282f8fdf" id="u282f8fdf"><span data-lake-id="u0b54240c" id="u0b54240c">不过在1.6中，方法区中包含了字符串常量池，而在1.7中，把字符串常量池、和静态变量都移到了堆内存中。这么做的主要原因是因为永久代的 GC 回收效率太低，只有在FullGC的时候才会被执行回收。但是Java中往往会有很多字符串也是朝生夕死的，将字符串常量池放到堆中，能够更高效及时地回收字符串内存</span></p>
  <p data-lake-id="ubfc2dd84" id="ubfc2dd84"><span data-lake-id="u92299d79" id="u92299d79">​</span><br></p>
  <p data-lake-id="u718eed95" id="u718eed95"><span data-lake-id="u565b09fe" id="u565b09fe">下图是1.6VS1.7的对比：</span></p>
  <p data-lake-id="u5aca8b02" id="u5aca8b02"><br></p>
  <p data-lake-id="u60c5c556" id="u60c5c556"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1669537898487-81279bca-9fbe-4f9d-9009-9758ec7a9922.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_26%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1669538120261-1fcfcdf1-d382-4cca-832d-76849f68837d.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_26%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ua43c679f" id="ua43c679f"><br></p>
  <p data-lake-id="uef555be6" id="uef555be6"><span data-lake-id="u1eb41cba" id="u1eb41cba">由于永久代有固定的大小，且不容易调整，因此在一些场景下容易导致内存溢出。例如，如果应用程序中使用大量的动态生成类或者频繁地加载卸载类，就可能导致永久代溢出。</span></p>
  <p data-lake-id="u4074b556" id="u4074b556"><span data-lake-id="u7b522a59" id="u7b522a59">​</span><br></p>
  <p data-lake-id="ud7656bca" id="ud7656bca"><span data-lake-id="uf0907b6e" id="uf0907b6e">所以，从JDK 1.8开始，HotSpot虚拟机对方法区的实现进行了重大改变。永久代被移除，取而代之的是</span><strong><span data-lake-id="u3937eaf6" id="u3937eaf6">元空间（Metaspace）</span></strong><span data-lake-id="ucdd43123" id="ucdd43123">。元空间是使用本地内存（Native Memory）来存储类的元数据信息的，它不再位于堆内存中。</span></p>
  <p data-lake-id="u3db23e07" id="u3db23e07"><br></p>
  <p data-lake-id="u81ea86b6" id="u81ea86b6"><img src="https://cdn.nlark.com/yuque/0/2022/png/719664/1669538288645-d3c13e5e-c2ad-4236-9843-1d558c76723a.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_26%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uc0861c1f" id="uc0861c1f"><br></p>
  <p data-lake-id="ue48e74f1" id="ue48e74f1"><span data-lake-id="u30b5fd85" id="u30b5fd85">元空间的特点是可以根据应用程序的需要动态调整其大小，因此更加灵活。它能够有效地避免了永久代的内存溢出问题，并且可以减少垃圾回收的压力。元空间的内存使用量受限于操作系统对本地内存的限制。</span></p>
 </body>
</html>